package handler

import (
	"errors"
	"fmt"
	"github.com/spf13/cast"
	"github.com/zhufuyi/sponge/pkg/jwt"
	"github.com/zhufuyi/sponge/pkg/utils"
	"shop/internal/global"
	"shop/internal/tools"
	"time"

	"shop/internal/cache"
	"shop/internal/dao"
	"shop/internal/ecode"
	"shop/internal/model"
	"shop/internal/types"

	"github.com/gin-gonic/gin"
	"github.com/jinzhu/copier"
	"github.com/zhufuyi/sponge/pkg/gin/middleware"
	"github.com/zhufuyi/sponge/pkg/gin/response"
	"github.com/zhufuyi/sponge/pkg/logger"
	"github.com/zhufuyi/sponge/pkg/mysql/query"
)

var _ TkMemberHandler = (*tkMemberHandler)(nil)

// TkMemberHandler defining the handler interface
type TkMemberHandler interface {
	Create(c *gin.Context)
	DeleteByID(c *gin.Context)
	DeleteByIDs(c *gin.Context)
	UpdateByID(c *gin.Context)
	GetByID(c *gin.Context)
	ListByIDs(c *gin.Context)
	List(c *gin.Context)
	Auth(c *gin.Context)
}
type tkMemberHandler struct {
	iDao                     dao.TkMemberDao
	AdDao                    dao.TkAdDao
	MemberOpenIDChangeLogDao dao.TkMemberOpenIDChangeLogDao
}

// NewTkMemberHandler creating the handler interface
func NewTkMemberHandler() TkMemberHandler {
	return &tkMemberHandler{
		iDao: dao.NewTkMemberDao(
			model.GetDB(),
			cache.NewTkMemberCache(model.GetCacheType()),
		),
		AdDao: dao.NewTkAdDao(
			model.GetDB(),
			cache.NewTkAdCache(model.GetCacheType()),
		),
		MemberOpenIDChangeLogDao: dao.NewTkMemberOpenIDChangeLogDao(
			model.GetDB(),
			cache.NewTkMemberOpenIDChangeLogCache(model.GetCacheType()),
		),
	}
}

func (h tkMemberHandler) Auth(c *gin.Context) {
	form := &types.AuthReq{}
	err := c.ShouldBindJSON(form)
	if err != nil {
		logger.Warn("ShouldBindJSON error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
		response.Error(c, ecode.InvalidParams.WithDetails(err.Error()))
		return
	}
	if form.Timestamp+60 < time.Now().Unix() {
		response.Error(c, ecode.ErrCodeFail)
	}
	ctx := c.Request.Context()
	tkMember := &model.TkMember{}
	err = copier.Copy(tkMember, form)
	if err != nil {
		response.Error(c, ecode.InvalidParams)
		return
	}

	//获取用户信息
	memberInfo, err := h.iDao.GetByWere(ctx, tkMember)
	//错误&&不是查询为空的就记录错误 抛出异常
	if err != nil && err != query.ErrNotFound {
		logger.Error("query", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
		response.Output(c, ecode.InternalServerError.ToHTTPCode())
		return
	}
	//没有查询到就开始注册用户
	if memberInfo == nil {
		//获取广告信息
		if form.AdCode != "" {
			adInfo, err := h.AdDao.GetByCode(ctx, form.AdCode)
			if err != nil && err != query.ErrNotFound {
				logger.Error("query GetByCode", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
				response.Output(c, ecode.InternalServerError.ToHTTPCode())
				return
			}
			if adInfo != nil {
				switch adInfo.PlatformID {
				case 4: //磁力
					tkMember.Source = cast.ToInt(global.AdChannelKs)
				default: //广点通
					tkMember.Source = cast.ToInt(global.AdChannelAdq)
				}
			}
		}
		tkMember.LastActiveTime = cast.ToInt(time.Now().Unix())
		tkMember.CreatedAt = cast.ToInt(time.Now().Unix())
		tkMember.Status = 1
		tkMember.MemberID = tools.GenerateDateNumber()
		err := h.iDao.Create(ctx, tkMember)
		if err != nil {
			logger.Error("创建会员失败", logger.Err(err), logger.Any("form", tkMember), middleware.GCtxRequestIDField(c))
			response.Output(c, ecode.InternalServerError.ToHTTPCode())
			return
		}
		//创建会员变动信息
		now := time.Now()
		changeLog := &model.TkMemberOpenIDChangeLog{
			UserID:    cast.ToInt(tkMember.ID),
			UnionID:   tkMember.UnionID,
			OpenID:    tkMember.OpenID,
			UpdatedAt: &now,
			CreatedAt: &now,
		}
		err = h.MemberOpenIDChangeLogDao.Create(ctx, changeLog)
		if err != nil {
			logger.Error("Create MemberOpenIDChangeLogDao", logger.Err(err), logger.Any("data", changeLog), middleware.GCtxRequestIDField(c))
			response.Output(c, ecode.InternalServerError.ToHTTPCode())
			return
		}
	} else {
		if memberInfo.OpenID != form.OpenID {
			condition := &model.TkMemberOpenIDChangeLog{
				UserID: cast.ToInt(memberInfo.ID),
				OpenID: form.OpenID,
			}
			were, err := h.MemberOpenIDChangeLogDao.GetByWere(ctx, condition)
			if err != nil && err != query.ErrNotFound {
				logger.Error("Get MemberOpenIDChangeLogDao", logger.Err(err), logger.Any("data", condition), middleware.GCtxRequestIDField(c))
				response.Output(c, ecode.InternalServerError.ToHTTPCode())
				return
			}
			now := time.Now()
			fmt.Printf("were : %# v\n", were)

			if were != nil {
				insertData := &model.TkMemberOpenIDChangeLog{
					UserID:    cast.ToInt(memberInfo.ID),
					OpenID:    memberInfo.OpenID,
					UnionID:   memberInfo.UnionID,
					UpdatedAt: &now,
					CreatedAt: &now,
				}
				err := h.MemberOpenIDChangeLogDao.Create(ctx, insertData)
				if err != nil {
					logger.Error("Create TkMemberOpenIDChangeLog", logger.Err(err), logger.Any("form", insertData), middleware.GCtxRequestIDField(c))
					response.Output(c, ecode.InternalServerError.ToHTTPCode())
					return
				}
			} else {
				were.UpdatedAt = &now
				err := h.MemberOpenIDChangeLogDao.UpdateByID(c, were)
				if err != nil {
					logger.Error("Update TkMemberOpenIDChangeLog", logger.Err(err), logger.Any("form", were), middleware.GCtxRequestIDField(c))
					response.Output(c, ecode.InternalServerError.ToHTTPCode())
					return
				}
			}

		}

		t := time.Now().Unix()
		tkMember.LastActiveTime = cast.ToInt(t)
		tkMember.UpdatedAt = cast.ToInt(t)
		tkMember.LastActiveTime = cast.ToInt(t)
		tkMember.ID = memberInfo.ID
		err := h.iDao.UpdateByID(ctx, tkMember)
		if err != nil {
			logger.Error("Update TkMember", logger.Err(err), logger.Any("form", tkMember), middleware.GCtxRequestIDField(c))
			response.Output(c, ecode.InternalServerError.ToHTTPCode())
			return
		}
	}

	reply := &types.AuthReply{}
	_ = copier.Copy(reply, tkMember)
	if len(reply.Phone) > 7 {
		reply.EncryPhone = tools.MaskPhoneNumber(reply.Phone)
	}
	if memberInfo.SearchHistory != "" {
		err := tools.JSONToStruct(tkMember.SearchHistory, &reply.SearchHistory)
		if err != nil {
			logger.Error("JSONToStruct error", logger.Err(err), middleware.GCtxRequestIDField(c))
			response.Output(c, ecode.InternalServerError.ToHTTPCode())
			return
		}
	}

	token, err := jwt.GenerateToken(cast.ToString(tkMember.ID), tkMember.OpenID, tkMember.AppID, tkMember.UnionID)
	if err != nil {
		logger.Error("jwt.GenerateToken error", logger.Err(err), middleware.GCtxRequestIDField(c))
		response.Output(c, ecode.InternalServerError.ToHTTPCode())
		return
	}

	reply.AccessToken = token
	response.Success(c, reply)
}

// Create a record
// @Summary create tkMember
// @Description submit information to create tkMember
// @Tags tkMember
// @accept json
// @Produce json
// @Param data body types.CreateTkMemberRequest true "tkMember information"
// @Success 200 {object} types.Result{}
// @Router /api/v1/tkMember [post]
func (h *tkMemberHandler) Create(c *gin.Context) {
	form := &types.CreateTkMemberRequest{}
	err := c.ShouldBindJSON(form)
	if err != nil {
		logger.Warn("ShouldBindJSON error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
		response.Error(c, ecode.InvalidParams)
		return
	}

	tkMember := &model.TkMember{}
	err = copier.Copy(tkMember, form)
	if err != nil {
		response.Error(c, ecode.ErrCreateTkMember)
		return
	}

	err = h.iDao.Create(c.Request.Context(), tkMember)
	if err != nil {
		logger.Error("Create error", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
		response.Output(c, ecode.InternalServerError.ToHTTPCode())
		return
	}

	response.Success(c, gin.H{"id": tkMember.ID})
}

// DeleteByID delete a record by id
// @Summary delete tkMember
// @Description delete tkMember by id
// @Tags tkMember
// @accept json
// @Produce json
// @Param id path string true "id"
// @Success 200 {object} types.Result{}
// @Router /api/v1/tkMember/{id} [delete]
func (h *tkMemberHandler) DeleteByID(c *gin.Context) {
	_, id, isAbort := getTkMemberIDFromPath(c)
	if isAbort {
		return
	}

	err := h.iDao.DeleteByID(c.Request.Context(), id)
	if err != nil {
		logger.Error("DeleteByID error", logger.Err(err), logger.Any("id", id), middleware.GCtxRequestIDField(c))
		response.Output(c, ecode.InternalServerError.ToHTTPCode())
		return
	}

	response.Success(c)
}

// DeleteByIDs delete records by batch id
// @Summary delete tkMembers
// @Description delete tkMembers by batch id
// @Tags tkMember
// @Param data body types.DeleteTkMembersByIDsRequest true "id array"
// @Accept json
// @Produce json
// @Success 200 {object} types.Result{}
// @Router /api/v1/tkMember/delete/ids [post]
func (h *tkMemberHandler) DeleteByIDs(c *gin.Context) {
	form := &types.DeleteTkMembersByIDsRequest{}
	err := c.ShouldBindJSON(form)
	if err != nil {
		logger.Warn("ShouldBindJSON error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
		response.Error(c, ecode.InvalidParams)
		return
	}

	err = h.iDao.DeleteByIDs(c.Request.Context(), form.IDs)
	if err != nil {
		logger.Error("GetByIDs error", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
		response.Output(c, ecode.InternalServerError.ToHTTPCode())
		return
	}

	response.Success(c)
}

// UpdateByID update information by id
// @Summary update tkMember
// @Description update tkMember information by id
// @Tags tkMember
// @accept json
// @Produce json
// @Param id path string true "id"
// @Param data body types.UpdateTkMemberByIDRequest true "tkMember information"
// @Success 200 {object} types.Result{}
// @Router /api/v1/tkMember/{id} [put]
func (h *tkMemberHandler) UpdateByID(c *gin.Context) {
	_, id, isAbort := getTkMemberIDFromPath(c)
	if isAbort {
		return
	}

	form := &types.UpdateTkMemberByIDRequest{}
	err := c.ShouldBindJSON(form)
	if err != nil {
		logger.Warn("ShouldBindJSON error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
		response.Error(c, ecode.InvalidParams)
		return
	}
	form.ID = id

	tkMember := &model.TkMember{}
	err = copier.Copy(tkMember, form)
	if err != nil {
		response.Error(c, ecode.ErrUpdateByIDTkMember)
		return
	}

	err = h.iDao.UpdateByID(c.Request.Context(), tkMember)
	if err != nil {
		logger.Error("UpdateByID error", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
		response.Output(c, ecode.InternalServerError.ToHTTPCode())
		return
	}

	response.Success(c)
}

// GetByID get a record by id
// @Summary get tkMember detail
// @Description get tkMember detail by id
// @Tags tkMember
// @Param id path string true "id"
// @Accept json
// @Produce json
// @Success 200 {object} types.Result{}
// @Router /api/v1/tkMember/{id} [get]
func (h *tkMemberHandler) GetByID(c *gin.Context) {
	idStr, id, isAbort := getTkMemberIDFromPath(c)
	if isAbort {
		return
	}

	tkMember, err := h.iDao.GetByID(c.Request.Context(), id)
	if err != nil {
		if errors.Is(err, query.ErrNotFound) {
			logger.Warn("GetByID not found", logger.Err(err), logger.Any("id", id), middleware.GCtxRequestIDField(c))
			response.Error(c, ecode.NotFound)
		} else {
			logger.Error("GetByID error", logger.Err(err), logger.Any("id", id), middleware.GCtxRequestIDField(c))
			response.Output(c, ecode.InternalServerError.ToHTTPCode())
		}
		return
	}

	data := &types.GetTkMemberByIDRespond{}
	err = copier.Copy(data, tkMember)
	if err != nil {
		response.Error(c, ecode.ErrGetByIDTkMember)
		return
	}
	data.ID = idStr

	response.Success(c, gin.H{"tkMember": data})
}

// ListByIDs list of records by batch id
// @Summary list of tkMembers by batch id
// @Description list of tkMembers by batch id
// @Tags tkMember
// @Param data body types.GetTkMembersByIDsRequest true "id array"
// @Accept json
// @Produce json
// @Success 200 {object} types.Result{}
// @Router /api/v1/tkMember/list/ids [post]
func (h *tkMemberHandler) ListByIDs(c *gin.Context) {
	form := &types.GetTkMembersByIDsRequest{}
	err := c.ShouldBindJSON(form)
	if err != nil {
		logger.Warn("ShouldBindJSON error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
		response.Error(c, ecode.InvalidParams)
		return
	}

	tkMemberMap, err := h.iDao.GetByIDs(c.Request.Context(), form.IDs)
	if err != nil {
		logger.Error("GetByIDs error", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
		response.Output(c, ecode.InternalServerError.ToHTTPCode())

		return
	}

	tkMembers := []*types.GetTkMemberByIDRespond{}
	for _, id := range form.IDs {
		if v, ok := tkMemberMap[id]; ok {
			record, err := convertTkMember(v)
			if err != nil {
				response.Error(c, ecode.ErrListTkMember)
				return
			}
			tkMembers = append(tkMembers, record)
		}
	}

	response.Success(c, gin.H{
		"tkMembers": tkMembers,
	})
}

// List of records by query parameters
// @Summary list of tkMembers by query parameters
// @Description list of tkMembers by paging and conditions
// @Tags tkMember
// @accept json
// @Produce json
// @Param data body types.Params true "query parameters"
// @Success 200 {object} types.Result{}
// @Router /api/v1/tkMember/list [post]
func (h *tkMemberHandler) List(c *gin.Context) {
	form := &types.GetTkMembersRequest{}
	err := c.ShouldBindJSON(form)
	if err != nil {
		logger.Warn("ShouldBindJSON error: ", logger.Err(err), middleware.GCtxRequestIDField(c))
		response.Error(c, ecode.InvalidParams)
		return
	}

	tkMembers, total, err := h.iDao.GetByColumns(c.Request.Context(), &form.Params)
	if err != nil {
		logger.Error("GetByColumns error", logger.Err(err), logger.Any("form", form), middleware.GCtxRequestIDField(c))
		response.Output(c, ecode.InternalServerError.ToHTTPCode())
		return
	}

	data, err := convertTkMembers(tkMembers)
	if err != nil {
		response.Error(c, ecode.ErrListTkMember)
		return
	}

	response.Success(c, gin.H{
		"tkMembers": data,
		"total":     total,
	})
}

func getTkMemberIDFromPath(c *gin.Context) (string, uint64, bool) {
	idStr := c.Param("id")
	id, err := utils.StrToUint64E(idStr)
	if err != nil || id == 0 {
		logger.Warn("StrToUint64E error: ", logger.String("idStr", idStr), middleware.GCtxRequestIDField(c))
		response.Error(c, ecode.InvalidParams)
		return "", 0, true
	}

	return idStr, id, false
}

func convertTkMember(tkMember *model.TkMember) (*types.GetTkMemberByIDRespond, error) {
	data := &types.GetTkMemberByIDRespond{}
	err := copier.Copy(data, tkMember)
	if err != nil {
		return nil, err
	}
	data.ID = utils.Uint64ToStr(tkMember.ID)
	return data, nil
}

func convertTkMembers(fromValues []*model.TkMember) ([]*types.GetTkMemberByIDRespond, error) {
	toValues := []*types.GetTkMemberByIDRespond{}
	for _, v := range fromValues {
		data, err := convertTkMember(v)
		if err != nil {
			return nil, err
		}
		toValues = append(toValues, data)
	}

	return toValues, nil
}
